vgacutan

Note: This involves getting data ready for analysis and doing some preliminary investigations.

Data

Explore a dataset that contains information about movies, including ratings, budget, gross revenue and other attributes.

x = 1:10
print(x^2)
 [1]   1   4   9  16  25  36  49  64  81 100

Plots appear inline too:

plot(x, x^2, 'o')

Load data

load('movies_merged')
df = movies_merged
cat("Dataset has", dim(df)[1], "rows and", dim(df)[2], "columns", end="\n", file="")
Dataset has 40789 rows and 39 columns 
colnames(df)
 [1] "Title"             "Year"              "Rated"             "Released"          "Runtime"           "Genre"            
 [7] "Director"          "Writer"            "Actors"            "Plot"              "Language"          "Country"          
[13] "Awards"            "Poster"            "Metascore"         "imdbRating"        "imdbVotes"         "imdbID"           
[19] "Type"              "tomatoMeter"       "tomatoImage"       "tomatoRating"      "tomatoReviews"     "tomatoFresh"      
[25] "tomatoRotten"      "tomatoConsensus"   "tomatoUserMeter"   "tomatoUserRating"  "tomatoUserReviews" "tomatoURL"        
[31] "DVD"               "BoxOffice"         "Production"        "Website"           "Response"          "Budget"           
[37] "Domestic_Gross"    "Gross"             "Date"             

Load R packages

library(ggplot2)
package ‘ggplot2’ was built under R version 3.3.2
library(GGally)
package ‘GGally’ was built under R version 3.3.2
library(splitstackshape)
Loading required package: data.table
data.table 1.10.4
**********
This installation of data.table has not detected OpenMP support. It will still work but in single-threaded mode. If this a Mac and you obtained the Mac binary of data.table from CRAN, CRAN's Mac does not yet support OpenMP. In the meantime please follow our Mac installation instructions on the data.table homepage. If it works and you observe benefits from multiple threads as others have reported, please convince Simon Ubanek by sending him evidence and ask him to turn on OpenMP support when CRAN builds package binaries for Mac. Alternatives are to install Ubuntu on your Mac (which I have done and works well) or use Windows where OpenMP is supported and works well.
**********
  The fastest way to learn (by data.table authors): https://www.datacamp.com/courses/data-analysis-the-data-table-way
  Documentation: ?data.table, example(data.table) and browseVignettes("data.table")
  Release notes, videos and slides: http://r-datatable.com
library(plyr)

1. Remove non-movie rows

The variable Type captures whether the row is a movie, a TV series, or a game. Remove all rows from df that do not correspond to movies.

moviesOnly = subset(df, Type == "movie")
cat("Number of Movie rows remaining:", dim(moviesOnly)[1])
Number of Movie rows remaining: 40000

2. Process Runtime column

The variable Runtime represents the length of the title as a string. Write R code to convert it to a numeric value (in minutes) and replace df$Runtime with the new numeric column.

p2q1 = moviesOnly
p2q1$Runtime = substr(p2q1$Runtime,0,nchar(p2q1$Runtime)-4)
p2q1$Runtime = as.numeric(as.character(p2q1$Runtime))
NAs introduced by coercion
str(p2q1[,5])
 num [1:40000] 70 21 106 75 14 82 65 NA 95 85 ...
df_moviesOnly =p2q1[c("Runtime", "Year", "Budget")]
df_moviesOnly=df_moviesOnly[complete.cases(df_moviesOnly),]
x.mean <- mean(df_moviesOnly$Runtime)
p1Q2 = ggplot(aes(x = Runtime), data = df_moviesOnly)
p1Q2 = p1Q2 + geom_histogram(alpha=0.5, color = "grey", binwidth = 5)
p1Q2 = p1Q2 + geom_vline(xintercept = c(x.mean), color = "red", alpha = 1)
p1Q2 = p1Q2 + geom_text(x = x.mean, y = 200, label = paste("Mean", round(x.mean, digits = 2), sep = '\n'), color = "red")
p1Q2 = p1Q2 + labs(title="p1Q2: Runtime Distribution", subtitle = "Movies Only Dataset")
print(p1Q2)

p2Q2 = qplot(log(Budget), log(Runtime), size=I(1), data =df_moviesOnly) + geom_smooth(method = "loess", se=F) + labs(title="p2Q2: Relationship between Movie Budget Vs Runtime", subtitle = "Movies Only Dataset")
print(p2Q2)

p3Q2 = ggplot(df_moviesOnly[,c("Year","Runtime")], aes(Year, Runtime)) + geom_point() + geom_smooth(method = "loess") + labs(title="p2Q3: Relationship between Runtime Vs Year", subtitle = "Movies Only Dataset")
print(p3Q2)

p4Q2 = ggplot(df_moviesOnly[,c("Year","Budget")], aes(Year, Budget)) + geom_point() + geom_smooth(method = "loess") + labs(title="p2Q4: Relationship between Budget Vs Year", subtitle = "Movies Only Dataset")
print(p4Q2)

Movie Runtime shows a normal distribution(Bell curve) with mean of 106.85 min [p1Q2]. The relationship between movie Budget and Runtime shows a slight increasing slope meaning an increase in Runtime would tend to increase a movie Budget . There are also some exceptions with low Runtime on high budget movies showing as an spars outliers in the graph [p2Q2]. Movie Runtime Vs Year produced is in roller coaster motion increasing from 1915 to about 1950 and decreasing slope to 2000 and comes back up beyong 2001- this is affected by number data points with less movies produced from later year and more movie produced onwards with varying runtimes [p3Q2]. The movie Budget tend to increase overtime since this is affected by dollar inflation. The overall relationship between movie Budget, Runtime and Year can be seen as increasing slope starting from Year 2001 to 2005.

3. Encode Genre column

The column Genre represents a list of genres associated with the movie in a string format. Write code to parse each text string into a binary vector with 1s representing the presence of a genre and 0s the absence, and add it to the dataframe as additional columns. Then remove the original Genre column.

For example, if there are a total of 3 genres: Drama, Comedy, and Action, a movie that is both Action and Comedy should be represented by a binary vector <0, 1, 1>. Note that you need to first compile a dictionary of all possible genres and then figure out which movie has which genres (you can use the R tm package to create the dictionary).

q3_moviesOnly =p2q1 #numeric runtime
q3_moviesOnly$Genre[q3_moviesOnly$Genre=="N/A"] = NA
q3_moviesOnly = subset(q3_moviesOnly, !is.na(q3_moviesOnly$Genre))
s = cSplit(q3_moviesOnly, "Genre", ", ", "long")
q3moviesOnly = dcast(s, ... ~ Genre, value.var = "Genre", fun.aggregate = length)
colnames(q3moviesOnly)
 [1] "Title"             "Year"              "Rated"             "Released"          "Runtime"           "Director"         
 [7] "Writer"            "Actors"            "Plot"              "Language"          "Country"           "Awards"           
[13] "Poster"            "Metascore"         "imdbRating"        "imdbVotes"         "imdbID"            "Type"             
[19] "tomatoMeter"       "tomatoImage"       "tomatoRating"      "tomatoReviews"     "tomatoFresh"       "tomatoRotten"     
[25] "tomatoConsensus"   "tomatoUserMeter"   "tomatoUserRating"  "tomatoUserReviews" "tomatoURL"         "DVD"              
[31] "BoxOffice"         "Production"        "Website"           "Response"          "Budget"            "Domestic_Gross"   
[37] "Gross"             "Date"              "Action"            "Adult"             "Adventure"         "Animation"        
[43] "Biography"         "Comedy"            "Crime"             "Documentary"       "Drama"             "Family"           
[49] "Fantasy"           "Film-Noir"         "Game-Show"         "History"           "Horror"            "Music"            
[55] "Musical"           "Mystery"           "News"              "Reality-TV"        "Romance"           "Sci-Fi"           
[61] "Short"             "Sport"             "Talk-Show"         "Thriller"          "War"               "Western"          

Plot the relative proportions of movies having the top 10 most common genres.

listAll=sort(unique(unlist(strsplit(as.character(q3_moviesOnly$Genre),", "))))
listAll_d = list()
for (column in listAll)
  listAll_d[[column]] <- sum(q3moviesOnly[,get(column)])
listAll_d = listAll_d[order(sapply(listAll_d, max))]
gtab = data.frame(melt(listAll_d))
gtab = head(gtab[order(gtab$value, decreasing = TRUE),],10)
gtab$GenrePct = gtab$value/sum(gtab$value)
print(gtab)
p3Q1 = ggplot(gtab, aes(L1, GenrePct)) + geom_bar(stat = "identity") + coord_flip() + xlab("Genre") + labs(title="p3Q1: Top10 most common Genres", subtitle = "Movies Only Dataset")
print(p3Q1)

Examine how the distribution of Runtime changes across genres for the top 10 most common genres.

mruntime = subset(q3moviesOnly, !is.na(q3moviesOnly$Runtime))
runtimeGenre = list()
for (column in gtab$L1)  #list top10 Genre vs Runtime values 
  runtimeGenre[[column]] <- mruntime$Runtime[mruntime[,get(column)]==1]
xGR=data.frame(melt(runtimeGenre))
p3Q2_a = ggplot(xGR, aes(value)) + geom_histogram(binwidth = 3) + facet_grid(L1~., scales = "free_y") + xlab("Runtime") + labs(title="p3Q2: Top10 most common Genres Vs Runtime", subtitle = "Movies Only Dataset")
print(p3Q2_a)

p3Q2_b = ggplot(xGR, aes(L1, value)) + geom_boxplot()  + coord_flip() + xlab("Genre") + ylab("Runtime") + labs(title="p3Q2: Top10 Genre Vs Runtime BoxPlot")
print(p3Q2_b)

Drama is the top 1st in the common Genre list followed by comedy. The runtime distribution of the top 10 Genres are normal distibution (bell curve) except for Comedy and Documentary which shows the presence of bimodality in the distribution. The presence of huge numbers of outliers in all the Genre affects any noticeable trends in the runtime but majority of the top 10 Genre almost have the same movie Runtime median ranges to approx 90-110 minutes but only few exceptions are Animation and Short.

4. Eliminate mismatched rows

The dataframe was put together by merging two different sources of data and it is possible that the merging process was inaccurate in some cases (the merge was done based on movie title, but there are cases of different movies with the same title). The first source’s release time was represented by the column Year (numeric representation of the year) and the second by the column Released (string representation of release date).

q4_moviesOnly =q3moviesOnly
q4_moviesOnly = subset(q4_moviesOnly, !is.na(q4_moviesOnly$Released))
q4_moviesOnly$Released = as.numeric(substr(q4_moviesOnly$Released, 0,regexpr( "-",q4_moviesOnly$Released)[1]-1))
unknown timezone 'zone/tz/2019a.1.0/zoneinfo/America/Los_Angeles'
yearMatch = subset(q4_moviesOnly, Released == Year | Gross>=0 | Released ==Year+1)
cat("Movie rows with mismatch Year-Released dates :", nrow(q4_moviesOnly) - nrow(yearMatch))
Movie rows with mismatch Year-Released dates : 1697

Removal logic are the following: Remove NA’s from the “Released”" column of the original dataset. Extract only the Year data in the “Released” dates using regexpr and substr and convert those into numeric. Compare column “Year” to the converted “Released” column containing only the Year data. All rows where “Year” column value that are not equal to the “Released” Year column value are mismatch rows and were removed. A little bit of slack on some mismatch rows where movie Gross are greater than or equal to zero are not included for removal. This ends up removing 1739 rows.

5. Explore Gross revenue

For the commercial success of a movie, production houses want to maximize Gross revenue. Investigate if Gross revenue is related to Budget, Runtime or Genre in any way.

Note: To get a meaningful relationship, you may have to partition the movies into subsets such as short vs. long duration, or by genre, etc.

q5df = subset(yearMatch, !is.na(Budget) & !is.na(Gross))
rf_df = data.frame(Budget = q5df$Budget,
                 Gross = q5df$Gross)
rf_df_plt = ggplot(rf_df, aes(Budget, Gross)) + geom_point() + geom_smooth(method = "lm") + labs(title="p5Q1: Movie Budget Vs Gross Revenue" , subtitle = "Movies Only Dataset") 
print(rf_df_plt)

q5df1 = subset(yearMatch, !is.na(Runtime) & !is.na(Gross))
rf_df1 = data.frame(Runtime = q5df1$Runtime, Gross = q5df1$Gross)
rf_df1_plt = ggplot(rf_df1, aes(Runtime, Gross)) + geom_point() + geom_smooth(method = "lm") + labs(title="p5Q1: Movie Runtime Vs Gross Revenue" , subtitle = "Movies Only Dataset") 
print(rf_df1_plt)

gbr_df = data.frame(Genre=character(), Budget=numeric(), Gross=numeric(), stringsAsFactors = FALSE)
for (column in gtab$L1) #top10 Genre list vs Budget vs Gross
  gbr_df <- rbind(gbr_df, data.frame(Genre = column, Budget= q5df$Budget[q5df[,get(column)] == 1], 
                             Gross= q5df$Gross[q5df[,get(column)] == 1]))
d = ggplot(gbr_df, aes(Budget, Gross, fill= Genre)) + geom_point() + geom_smooth(method = "lm") + facet_grid(~Genre, scales = "free_y")
cors = ddply(gbr_df, c("Genre"), summarise, cor = round(cor(Budget, Gross), 2))
dplt = d + geom_text(data=cors, aes(label=paste("r=", cor, sep="")), x=2e+08, y=2e+09)
print(dplt)

It is interesting to see in the plot that movie Budget has an impact to the revenues produced by the Movies. Increase in Budget tend to increase revenue which make sense in reality since there are high budget films that are top Grossers although this can be also be affected by the quality of the story, the cast and other resources. By plotting the Gross Vs Budget per top 10 common Genre, it can be seen that Action, Adventure and Thriller films has a high R value which the producer can allocate budget to produce quality Action, Thriller and Adventure movies that has the potential to produce high revenues. Documentary movies also has a high R value that has potential to produce revenue as well. The overall movie Gross revenue per month tend to follow a sinusoidal pattern where Gross revenue are low on months Jan-Apr, high on months May-June, low on succeding months and comes back high on Nov-Dec.

q5 = moviesOnly
q5df2 = subset(q5, !is.na(Released) & !is.na(Gross))
q5df2$Released = as.numeric(substr(q5df2$Released,regexpr( "-",q5df2$Released)[1]+1, regexpr( "-",q5df2$Released)[1]+2))
df_rg = c(q5df2$Gross, q5df2$Released)
df_rg=data.frame(q5df2[c("Released", "Gross")])
df_rg_plt=ggplot(df_rg, aes(x=Released, y=Gross)) + geom_point(aes(colour=Gross))+ scale_x_continuous(breaks = seq(1,12, by=1)) +  scale_colour_gradient(low = "red", high = "green") + xlab("Released Month") + labs(title="p5Q2: Movie Released Month Vs Gross Revenue" , subtitle = "Movies Only Dataset") 
print(df_rg_plt)

6. Process Awards column

The variable Awards describes nominations and awards in text format. Convert it to 2 numeric columns, the first capturing the number of wins, and the second capturing nominations. Replace the Awards column with these new columns, and then study the relationship of Gross revenue with respect to them.

Note that the format of the Awards column is not standard; you may have to use regular expressions to find the relevant values.

q6 = yearMatch
q6$Awards[q6$Awards == 'N/A'] = 0
q6df = data.frame(Awards = q6$Awards, Gross = q6$Gross)
q6df$wins = as.numeric(substr(q6df$Awards, regexpr("win", q6df$Awards)-3,regexpr("win", q6df$Awards)-1))
q6df$nominations = as.numeric(substr(q6df$Awards, regexpr("nomination", q6df$Awards)-3,regexpr("nomination", q6df$Awards)-1))
cat("valid/non-zero wins or nominations :", nrow(subset(q6df, !is.na(nominations) |!is.na(wins))))
valid/non-zero wins or nominations : 12905
winsNom_tab = q6df[complete.cases(q6df),]
str(winsNom_tab[,-1]) 
'data.frame':   2689 obs. of  3 variables:
 $ Gross      : num  6.69e+07 0.00 0.00 1.81e+08 6.02e+07 ...
 $ wins       : num  1 1 16 33 23 4 3 1 4 3 ...
 $ nominations: num  4 5 8 12 40 8 4 1 4 13 ...

Dataset used is the movie only data set from question 4. Number of wins and nominations in the Award column are extracted using regexpr and substr. Awards column char “N/A” values are converte to 0, then extracting the numbers before the “wins”" and “nomination” charaters, separating those to two columns and converting the result to numeric. Valid/non-zero wind ends up to 12905 rows.

q6df1 = subset(q6df,!is.na(Gross))
q6df1[is.na(q6df1)] <- 0 #convering all NA in wins and nominations column to 0
q5df1_plt = qplot(log(Gross), wins, size=I(1), data = q6df1, geom = "jitter") +labs(title="p5Q2: Gross Revenue Vs Award Winnings" , subtitle = "Movies Only Dataset") 
print(q5df1_plt)

q5df1_plt1 = qplot(log(Gross), nominations, size=I(1), data = q6df1, geom = "jitter")+labs(title="p5Q2: Gross Revenue Vs Award Nominations" , subtitle = "Movies Only Dataset") 
print(q5df1_plt1)

Movies that are Award winner and received nominations had increased in revenues. This make sense because movies that received more Award winnings and nominations are good quality movies since these awards are being streamlined by movie Academy boards. Good quality movies tend to increase movie Gross revenues.

7. Movie ratings from IMDb and Rotten Tomatoes

q7 = yearMatch
q7df = data.frame(Gross = q7$Gross, ImdbRating = q7$imdbRating, tomatoRating = q7$tomatoRating, tomatoUserRating = q7$tomatoUserRating)
q7df =q7df[complete.cases(q7df),]
q7df_plt1= ggplot(q7df, aes(x=ImdbRating, y=tomatoRating)) + geom_point() + geom_smooth(method = "loess") + labs(title="Movie Tomato Rating Vs IMDb Rating", subtitle = "Movies Only Dataset")
print(q7df_plt1)

q7df_plt2= ggplot(q7df, aes(x=ImdbRating, y=tomatoUserRating)) + geom_point() + geom_smooth(method = "loess") + labs(title="Movie Tomato User Rating Vs IMDb Rating", subtitle = "Movies Only Dataset")
print(q7df_plt2)

q7df_plt3= ggplot(q7df, aes(x=ImdbRating, y=Gross)) + geom_point() + geom_smooth(method = "loess") + labs(title="Movie Tomato User Rating Vs IMDb Rating", subtitle = "Movies Only Dataset")
print(q7df_plt3)

q7df_plt4= ggplot(q7df, aes(x=tomatoRating, y=Gross)) + geom_point() + geom_smooth(method = "loess") + labs(title="Movie Tomato User Rating Vs IMDb Rating", subtitle = "Movies Only Dataset")
print(q7df_plt4)

The disparity is not that high between IMDb rating and tomato ratings. They are both propotional by looking at the trends in the graph- propotional being if one of them increases the other rating increases. As per description in the website, IMDB weights the average based on how people fill out ratings and to avoid “vote stuffing” (weighted average rating). Rotten tomatoes on the other hand, count the percentage of users that rate the movie at at a certain threshold that clearly think the movie is “good”. In terms of Gross revenue produce per ratings, movie Gross revenue increase with increase in rating. IMDB data points with high Gross revenue are more focused within 6.0 to 7.0 rating while tomato rating are more scattered within around 3.5 to 7.5 becaue tomato rating threshold for “good” movies" starts at 3.5 and higher.

8. Ratings and awards

These ratings typically reflect the general appeal of the movie to the public or gather opinions from a larger body of critics. Whereas awards are given by professional societies that may evaluate a movie on specific attributes, such as artistic performance, screenplay, sound design, etc.

q8 = yearMatch
#q6df = subset(q6, !is.na(Gross))
q8$Awards[q6$Awards == 'N/A'] = 0
q8df = data.frame(Awards = q8$Awards, ImdbRating = q8$imdbRating, tomatoRating = q8$tomatoRating, tomatoUserRating = q8$tomatoUserRating )
q8df$wins = as.numeric(substr(q6df$Awards, regexpr("win", q6df$Awards)-3,regexpr("win", q6df$Awards)-1))
q8df$nominations = as.numeric(substr(q6df$Awards, regexpr("nomination", q6df$Awards)-3,regexpr("nomination", q6df$Awards)-1))
q8df =q8df[complete.cases(q8df),]
q8_tab =q8df[, -1]
q8_tab_plt1 = ggpairs(q8_tab) #correlation matrix
print(q8_tab_plt1)

 plot: [1,1] [====--------------------------------------------------------------------------------------------------]  4% est: 0s 
 plot: [1,2] [========----------------------------------------------------------------------------------------------]  8% est: 2s 
 plot: [1,3] [============------------------------------------------------------------------------------------------] 12% est: 2s 
 plot: [1,4] [================--------------------------------------------------------------------------------------] 16% est: 2s 
 plot: [1,5] [====================----------------------------------------------------------------------------------] 20% est: 2s 
 plot: [2,1] [========================------------------------------------------------------------------------------] 24% est: 2s 
 plot: [2,2] [=============================-------------------------------------------------------------------------] 28% est: 2s 
 plot: [2,3] [=================================---------------------------------------------------------------------] 32% est: 2s 
 plot: [2,4] [=====================================-----------------------------------------------------------------] 36% est: 1s 
 plot: [2,5] [=========================================-------------------------------------------------------------] 40% est: 1s 
 plot: [3,1] [=============================================---------------------------------------------------------] 44% est: 1s 
 plot: [3,2] [=================================================-----------------------------------------------------] 48% est: 1s 
 plot: [3,3] [=====================================================-------------------------------------------------] 52% est: 1s 
 plot: [3,4] [=========================================================---------------------------------------------] 56% est: 1s 
 plot: [3,5] [=============================================================-----------------------------------------] 60% est: 1s 
 plot: [4,1] [=================================================================-------------------------------------] 64% est: 1s 
 plot: [4,2] [=====================================================================---------------------------------] 68% est: 1s 
 plot: [4,3] [=========================================================================-----------------------------] 72% est: 1s 
 plot: [4,4] [==============================================================================------------------------] 76% est: 1s 
 plot: [4,5] [==================================================================================--------------------] 80% est: 0s 
 plot: [5,1] [======================================================================================----------------] 84% est: 0s 
 plot: [5,2] [==========================================================================================------------] 88% est: 0s 
 plot: [5,3] [==============================================================================================--------] 92% est: 0s 
 plot: [5,4] [==================================================================================================----] 96% est: 0s 
 plot: [5,5] [======================================================================================================]100% est: 0s 
                                                                                                                                  

q8_tab_plt2 = ggplot(q8_tab, aes(x=ImdbRating, y=wins)) + geom_point(aes(colour=wins)) + scale_colour_gradient(low = "red", high = "green")
print(q8_tab_plt2)

q8_tab_plt2 = ggplot(q8_tab, aes(x=tomatoRating, y=wins)) + geom_point(aes(colour=wins)) + scale_colour_gradient(low = "red", high = "green")
print(q8_tab_plt2)

There are noticeable correlation between the different rating (IMDb and Tomato) againts the number of Awards (wins and nomination) recived. High IMDb rating around 6.5 to 7.5 received high numbers of Award winnings and nominations. Tomato ratings with high Award winning and nominations are within 7.5 to 8.5. The disparity between the rating are not that high.

9. Expected insights

q9_df = data.frame(Language = yearMatch$Language, Budget = yearMatch$Budget, Gross = yearMatch$Gross)
a = cSplit(q9_df, "Language", ", ", "long")
q9_df = dcast(a, ... ~ Language, value.var = "Language", fun.aggregate = length)
langAll=sort(unique(unlist(strsplit(as.character(yearMatch$Language),", "))))
langAll_dct = list()
langAll = langAll[langAll!= " Ancient (to 1453)"]
langAll = langAll[langAll!= " Old"]
for (column in langAll)
  langAll_dct[[column]] <- sum(q9_df[,get(column)])
langAll_dct = langAll_dct[order(sapply(langAll_dct, max))]
langAll_dct_tab = na.omit(data.frame(melt(langAll_dct)))
langAll_dct_tab[langAll_dct_tab=="N/A"] = NA
langAll_dct_tab=langAll_dct_tab[complete.cases(langAll_dct_tab),]
langAll_dct_tab = head(langAll_dct_tab[order(langAll_dct_tab$value, decreasing = TRUE),],10)
langAll_dct_tab$LanguagePct = langAll_dct_tab$value/sum(langAll_dct_tab$value)
print(langAll_dct_tab)
p9Q1_plt = ggplot(langAll_dct_tab, aes(L1, LanguagePct)) + geom_bar(stat = "identity") + coord_flip() + xlab("Movie Language") + labs(title="p9Q1: Top10 most common movies Langues", subtitle = "Movies Only Dataset")
print(p9Q1_plt)

q9_df1 = data.frame(Language=character(), Gross=numeric(), Budget=numeric(), stringsAsFactors = FALSE)
for (column in langAll_dct_tab$L1) 
  q9_df1 <- rbind(q9_df1, data.frame(Language = column, Gross= q9_df$Gross[q9_df[,get(column)] == 1], 
                             Budget= q9_df$Budget[q9_df[,get(column)] == 1]))
dat = ggplot(q9_df1, aes(Budget, Gross, fill= Language)) + geom_point() + geom_smooth(method = "lm") + facet_grid(~Language, scales = "free_y")
cors = ddply(q9_df1, c("Language"), summarise, cor = round(cor(Budget, Gross), 2))
dat_dplt = dat + geom_text(data=cors, aes(label=paste("r=", cor, sep="")), x=2e+08, y=2e+09)
print(dat_dplt)

Movies in English language are the most common movies in the data set. This constitutes around 75% of the total common top 10 language movies followed by French at 59%.

Both movie Gross and Budget are highly coorrelated across all the top 10 movie languages. Inpsite of the English movies being the top commong movie languages, it has the lowest correlation at r=74% and Italian is the highest at r=82%. It does make sense that high budget movies tend to produces good revenues as more dollar can be allocated to resources like good/famous Actors, movie locations, equipements, special effects and staffs. There are also some exceptions were few high budgeted English movies that where flopped for some reason which affect the r value for the English movies to be slightly lower than the rest the top ten common movie languages.

10. Unexpected insight

q10_df = data.frame(Language = yearMatch$Language, Budget = yearMatch$Budget, Gross = yearMatch$Gross, ImdbRating = yearMatch$imdbRating, tomatoRating = yearMatch$tomatoRating, tomatoUserRating = yearMatch$tomatoUserRating)
b = cSplit(q10_df, "Language", ", ", "long")
q10_df = dcast(b, ... ~ Language, value.var = "Language", fun.aggregate = length)
q10_df1 = data.frame(Language=character(), Budget=numeric(), Gross=numeric(), ImdbRating=numeric(), tomatoRating=numeric(), tomatoUserRating=numeric(),  stringsAsFactors = FALSE)
for (column in langAll_dct_tab$L1)
  q10_df1 <- rbind(q10_df1, data.frame(Language = column, Budget= q10_df$Budget[q10_df[,get(column)] == 1],
                                     Gross= q10_df$Gross[q10_df[,get(column)] == 1],#, 
                                     ImdbRating = q10_df$ImdbRating[q10_df[,get(column)] == 1],
                                     tomatoRating = q10_df$tomatoRating[q10_df[,get(column)] == 1], 
                                     tomatoUserRating = q10_df$tomatoUserRating[q10_df[,get(column)] == 1]))
q10_df1 =q10_df1[complete.cases(q10_df1),]
dat10 = ggplot(q10_df1, aes(ImdbRating, tomatoRating, fill= Language)) + geom_point() + geom_smooth(method = "lm") + facet_grid(~Language, scales = "free_y")
cors = ddply(q10_df1, c("Language"), summarise, cor = round(cor(ImdbRating, tomatoRating), 2))
dat10_dplt = dat10 + geom_text(data=cors, aes(label=paste("r=", cor, sep="")), x=3, y=1)
print(dat10_dplt)

m1 = ddply(q10_df1, .(Language), summarize, med = median(ImdbRating))
p10_plt1 = ggplot(q10_df1, aes(x=Language,y=  ImdbRating)) + geom_boxplot() + geom_text(data = m1, aes(y = med, label = round(med,2)),size = 3, vjust = -0.5) + labs(title="p10Q1: Top10 most common movies Langues Vs ImdbRating", subtitle = "Movies Only Dataset")
print(p10_plt1)

  
m2 = ddply(q10_df1, .(Language), summarize, med = median(tomatoRating))
p10_plt2 = ggplot(q10_df1, aes(x=Language,y=  tomatoRating)) + geom_boxplot() + geom_text(data = m2, aes(y = med, label = round(med,2)),size = 3,vjust = -0.5) + 
  labs(title="p10Q1: Top10 most common movies Langues Vs tomatoRating", subtitle = "Movies Only Dataset")
print(p10_plt2)

m3 = ddply(q10_df1, .(Language), summarize, med = median(tomatoUserRating))
p10_plt3 = ggplot(q10_df1, aes(x=Language,y=  tomatoUserRating)) + geom_boxplot() + geom_text(data = m3, aes(y = med, label = round(med,2)),size = 3,vjust = -0.5) + 
  labs(title="p10Q1: Top10 most common movies Langues Vs tomatoUserRating", subtitle = "Movies Only Dataset")
print(p10_plt3)

Surprising to see that Chinese movies in Mandarain or Cantonese langueages are not in the top ten common movies in the dataset. Inspite of being a large country, I know that there lots of movies produced per year and have seen a lot of them that were dubbed in English like those famous Actors like Jackie Chan, Bruce Lee or Jet Li. I think that further investgation are needed about the Language as category in the database i.e is the language are based on the orignal language spoken or dubbed in English?. In addition, based on the above box plots, surprising to see that among the common top ten language, English language movies is one of among the lowest in all the ratings inspite of being on the top common movie languages. English language movies IMDb rating median of 6.5 (highest are Hindi, Polish at 7.15 & 7 ) , English language movies Tomato rating median of 5.8 (highest is Polish at 6.55) and English language movies Tomato User rating median of 3.3 (highest are Hindi, Polish at 3.65 & 3.55 ). Both Hindi or Polish seems to be consistently highesh among for all the 3 ratings.

LS0tCnRpdGxlOiAnRXhwbG9yZSBhbmQgUHJlcGFyZSBEYXRhJwpvdXRwdXQ6IAogIGh0bWxfbm90ZWJvb2s6IGRlZmF1bHQKICBodG1sX2RvY3VtZW50OiBkZWZhdWx0CgotLS0KIyMjIyB2Z2FjdXRhbgpfTm90ZTogVGhpcyBpbnZvbHZlcyBnZXR0aW5nIGRhdGEgcmVhZHkgZm9yIGFuYWx5c2lzIGFuZCBkb2luZyBzb21lIHByZWxpbWluYXJ5IGludmVzdGlnYXRpb25zLl8KCiMgRGF0YQoKRXhwbG9yZSBhIGRhdGFzZXQgdGhhdCBjb250YWlucyBpbmZvcm1hdGlvbiBhYm91dCBtb3ZpZXMsIGluY2x1ZGluZyByYXRpbmdzLCBidWRnZXQsIGdyb3NzIHJldmVudWUgYW5kIG90aGVyIGF0dHJpYnV0ZXMuCgoKYGBge3J9CnggPSAxOjEwCnByaW50KHheMikKYGBgCgpQbG90cyBhcHBlYXIgaW5saW5lIHRvbzoKYGBge3J9CnBsb3QoeCwgeF4yLCAnbycpCmBgYAoKCiMjIExvYWQgZGF0YQoKYGBge3J9CmxvYWQoJ21vdmllc19tZXJnZWQnKQpgYGAKCgpgYGB7cn0KZGYgPSBtb3ZpZXNfbWVyZ2VkCmNhdCgiRGF0YXNldCBoYXMiLCBkaW0oZGYpWzFdLCAicm93cyBhbmQiLCBkaW0oZGYpWzJdLCAiY29sdW1ucyIsIGVuZD0iXG4iLCBmaWxlPSIiKQpjb2xuYW1lcyhkZikKYGBgCgojIyBMb2FkIFIgcGFja2FnZXMKCmBgYHtyfQpsaWJyYXJ5KGdncGxvdDIpCmxpYnJhcnkoR0dhbGx5KQpsaWJyYXJ5KHNwbGl0c3RhY2tzaGFwZSkKbGlicmFyeShwbHlyKQpgYGAKCiMjIDEuIFJlbW92ZSBub24tbW92aWUgcm93cwoKVGhlIHZhcmlhYmxlIGBUeXBlYCBjYXB0dXJlcyB3aGV0aGVyIHRoZSByb3cgaXMgYSBtb3ZpZSwgYSBUViBzZXJpZXMsIG9yIGEgZ2FtZS4gUmVtb3ZlIGFsbCByb3dzIGZyb20gYGRmYCB0aGF0IGRvIG5vdCBjb3JyZXNwb25kIHRvIG1vdmllcy4KCmBgYHtyfQptb3ZpZXNPbmx5ID0gc3Vic2V0KGRmLCBUeXBlID09ICJtb3ZpZSIpCmNhdCgiTnVtYmVyIG9mIE1vdmllIHJvd3MgcmVtYWluaW5nOiIsIGRpbShtb3ZpZXNPbmx5KVsxXSkKYGBgCgojIyAyLiBQcm9jZXNzIGBSdW50aW1lYCBjb2x1bW4KClRoZSB2YXJpYWJsZSBgUnVudGltZWAgcmVwcmVzZW50cyB0aGUgbGVuZ3RoIG9mIHRoZSB0aXRsZSBhcyBhIHN0cmluZy4gV3JpdGUgUiBjb2RlIHRvIGNvbnZlcnQgaXQgdG8gYSBudW1lcmljIHZhbHVlIChpbiBtaW51dGVzKSBhbmQgcmVwbGFjZSBgZGYkUnVudGltZWAgd2l0aCB0aGUgbmV3IG51bWVyaWMgY29sdW1uLgoKYGBge3J9CnAycTEgPSBtb3ZpZXNPbmx5CnAycTEkUnVudGltZSA9IHN1YnN0cihwMnExJFJ1bnRpbWUsMCxuY2hhcihwMnExJFJ1bnRpbWUpLTQpCnAycTEkUnVudGltZSA9IGFzLm51bWVyaWMoYXMuY2hhcmFjdGVyKHAycTEkUnVudGltZSkpCnN0cihwMnExWyw1XSkKYGBgCgpgYGB7cn0KZGZfbW92aWVzT25seSA9cDJxMVtjKCJSdW50aW1lIiwgIlllYXIiLCAiQnVkZ2V0IildCmRmX21vdmllc09ubHk9ZGZfbW92aWVzT25seVtjb21wbGV0ZS5jYXNlcyhkZl9tb3ZpZXNPbmx5KSxdCngubWVhbiA8LSBtZWFuKGRmX21vdmllc09ubHkkUnVudGltZSkKCnAxUTIgPSBnZ3Bsb3QoYWVzKHggPSBSdW50aW1lKSwgZGF0YSA9IGRmX21vdmllc09ubHkpCnAxUTIgPSBwMVEyICsgZ2VvbV9oaXN0b2dyYW0oYWxwaGE9MC41LCBjb2xvciA9ICJncmV5IiwgYmlud2lkdGggPSA1KQpwMVEyID0gcDFRMiArIGdlb21fdmxpbmUoeGludGVyY2VwdCA9IGMoeC5tZWFuKSwgY29sb3IgPSAicmVkIiwgYWxwaGEgPSAxKQpwMVEyID0gcDFRMiArIGdlb21fdGV4dCh4ID0geC5tZWFuLCB5ID0gMjAwLCBsYWJlbCA9IHBhc3RlKCJNZWFuIiwgcm91bmQoeC5tZWFuLCBkaWdpdHMgPSAyKSwgc2VwID0gJ1xuJyksIGNvbG9yID0gInJlZCIpCnAxUTIgPSBwMVEyICsgbGFicyh0aXRsZT0icDFRMjogUnVudGltZSBEaXN0cmlidXRpb24iLCBzdWJ0aXRsZSA9ICJNb3ZpZXMgT25seSBEYXRhc2V0IikKcHJpbnQocDFRMikKCnAyUTIgPSBxcGxvdChsb2coQnVkZ2V0KSwgbG9nKFJ1bnRpbWUpLCBzaXplPUkoMSksIGRhdGEgPWRmX21vdmllc09ubHkpICsgZ2VvbV9zbW9vdGgobWV0aG9kID0gImxvZXNzIiwgc2U9RikgKyBsYWJzKHRpdGxlPSJwMlEyOiBSZWxhdGlvbnNoaXAgYmV0d2VlbiBNb3ZpZSBCdWRnZXQgVnMgUnVudGltZSIsIHN1YnRpdGxlID0gIk1vdmllcyBPbmx5IERhdGFzZXQiKQpwcmludChwMlEyKQoKcDNRMiA9IGdncGxvdChkZl9tb3ZpZXNPbmx5WyxjKCJZZWFyIiwiUnVudGltZSIpXSwgYWVzKFllYXIsIFJ1bnRpbWUpKSArIGdlb21fcG9pbnQoKSArIGdlb21fc21vb3RoKG1ldGhvZCA9ICJsb2VzcyIpICsgbGFicyh0aXRsZT0icDJRMzogUmVsYXRpb25zaGlwIGJldHdlZW4gUnVudGltZSBWcyBZZWFyIiwgc3VidGl0bGUgPSAiTW92aWVzIE9ubHkgRGF0YXNldCIpCnByaW50KHAzUTIpCgpwNFEyID0gZ2dwbG90KGRmX21vdmllc09ubHlbLGMoIlllYXIiLCJCdWRnZXQiKV0sIGFlcyhZZWFyLCBCdWRnZXQpKSArIGdlb21fcG9pbnQoKSArIGdlb21fc21vb3RoKG1ldGhvZCA9ICJsb2VzcyIpICsgbGFicyh0aXRsZT0icDJRNDogUmVsYXRpb25zaGlwIGJldHdlZW4gQnVkZ2V0IFZzIFllYXIiLCBzdWJ0aXRsZSA9ICJNb3ZpZXMgT25seSBEYXRhc2V0IikKcHJpbnQocDRRMikKYGBgCgoKCgpNb3ZpZSBSdW50aW1lIHNob3dzIGEgbm9ybWFsIGRpc3RyaWJ1dGlvbihCZWxsIGN1cnZlKSB3aXRoIG1lYW4gb2YgMTA2Ljg1IG1pbiBbcDFRMl0uICBUaGUgcmVsYXRpb25zaGlwIGJldHdlZW4gbW92aWUgQnVkZ2V0IGFuZCBSdW50aW1lIHNob3dzIGEgc2xpZ2h0IGluY3JlYXNpbmcgc2xvcGUgbWVhbmluZyBhbiBpbmNyZWFzZSBpbiBSdW50aW1lIHdvdWxkIHRlbmQgdG8gaW5jcmVhc2UgYSBtb3ZpZSBCdWRnZXQgLiBUaGVyZSBhcmUgYWxzbyBzb21lIGV4Y2VwdGlvbnMgd2l0aCBsb3cgUnVudGltZSBvbiBoaWdoIGJ1ZGdldCBtb3ZpZXMgc2hvd2luZyBhcyBhbiBzcGFycyBvdXRsaWVycyBpbiB0aGUgZ3JhcGggW3AyUTJdLiAgTW92aWUgUnVudGltZSBWcyBZZWFyIHByb2R1Y2VkIGlzIGluIHJvbGxlciBjb2FzdGVyIG1vdGlvbiBpbmNyZWFzaW5nIGZyb20gMTkxNSB0byBhYm91dCAxOTUwIGFuZCBkZWNyZWFzaW5nIHNsb3BlIHRvIDIwMDAgYW5kIGNvbWVzIGJhY2sgdXAgYmV5b25nIDIwMDEtIHRoaXMgaXMgYWZmZWN0ZWQgYnkgbnVtYmVyIGRhdGEgcG9pbnRzIHdpdGggbGVzcyBtb3ZpZXMgcHJvZHVjZWQgZnJvbSBsYXRlciB5ZWFyIGFuZCBtb3JlIG1vdmllIHByb2R1Y2VkIG9ud2FyZHMgd2l0aCB2YXJ5aW5nIHJ1bnRpbWVzIFtwM1EyXS4gIFRoZSBtb3ZpZSBCdWRnZXQgdGVuZCB0byBpbmNyZWFzZSBvdmVydGltZSBzaW5jZSB0aGlzIGlzIGFmZmVjdGVkIGJ5IGRvbGxhciBpbmZsYXRpb24uICBUaGUgb3ZlcmFsbCByZWxhdGlvbnNoaXAgYmV0d2VlbiBtb3ZpZSBCdWRnZXQsIFJ1bnRpbWUgYW5kIFllYXIgY2FuIGJlIHNlZW4gYXMgaW5jcmVhc2luZyBzbG9wZSBzdGFydGluZyBmcm9tIFllYXIgMjAwMSB0byAyMDA1LiAKCiMjIDMuIEVuY29kZSBgR2VucmVgIGNvbHVtbgoKVGhlIGNvbHVtbiBgR2VucmVgIHJlcHJlc2VudHMgYSBsaXN0IG9mIGdlbnJlcyBhc3NvY2lhdGVkIHdpdGggdGhlIG1vdmllIGluIGEgc3RyaW5nIGZvcm1hdC4gV3JpdGUgY29kZSB0byBwYXJzZSBlYWNoIHRleHQgc3RyaW5nIGludG8gYSBiaW5hcnkgdmVjdG9yIHdpdGggMXMgcmVwcmVzZW50aW5nIHRoZSBwcmVzZW5jZSBvZiBhIGdlbnJlIGFuZCAwcyB0aGUgYWJzZW5jZSwgYW5kIGFkZCBpdCB0byB0aGUgZGF0YWZyYW1lIGFzIGFkZGl0aW9uYWwgY29sdW1ucy4gVGhlbiByZW1vdmUgdGhlIG9yaWdpbmFsIGBHZW5yZWAgY29sdW1uLgoKRm9yIGV4YW1wbGUsIGlmIHRoZXJlIGFyZSBhIHRvdGFsIG9mIDMgZ2VucmVzOiBEcmFtYSwgQ29tZWR5LCBhbmQgQWN0aW9uLCBhIG1vdmllIHRoYXQgaXMgYm90aCBBY3Rpb24gYW5kIENvbWVkeSBzaG91bGQgYmUgcmVwcmVzZW50ZWQgYnkgYSBiaW5hcnkgdmVjdG9yIDwwLCAxLCAxPi4gTm90ZSB0aGF0IHlvdSBuZWVkIHRvIGZpcnN0IGNvbXBpbGUgYSBkaWN0aW9uYXJ5IG9mIGFsbCBwb3NzaWJsZSBnZW5yZXMgYW5kIHRoZW4gZmlndXJlIG91dCB3aGljaCBtb3ZpZSBoYXMgd2hpY2ggZ2VucmVzICh5b3UgY2FuIHVzZSB0aGUgUiBgdG1gIHBhY2thZ2UgdG8gY3JlYXRlIHRoZSBkaWN0aW9uYXJ5KS4KCmBgYHtyfQpxM19tb3ZpZXNPbmx5ID1wMnExICNudW1lcmljIHJ1bnRpbWUKcTNfbW92aWVzT25seSRHZW5yZVtxM19tb3ZpZXNPbmx5JEdlbnJlPT0iTi9BIl0gPSBOQQpxM19tb3ZpZXNPbmx5ID0gc3Vic2V0KHEzX21vdmllc09ubHksICFpcy5uYShxM19tb3ZpZXNPbmx5JEdlbnJlKSkKcyA9IGNTcGxpdChxM19tb3ZpZXNPbmx5LCAiR2VucmUiLCAiLCAiLCAibG9uZyIpCnEzbW92aWVzT25seSA9IGRjYXN0KHMsIC4uLiB+IEdlbnJlLCB2YWx1ZS52YXIgPSAiR2VucmUiLCBmdW4uYWdncmVnYXRlID0gbGVuZ3RoKQpjb2xuYW1lcyhxM21vdmllc09ubHkpCgpgYGAKClBsb3QgdGhlIHJlbGF0aXZlIHByb3BvcnRpb25zIG9mIG1vdmllcyBoYXZpbmcgdGhlIHRvcCAxMCBtb3N0IGNvbW1vbiBnZW5yZXMuCgpgYGB7cn0KbGlzdEFsbD1zb3J0KHVuaXF1ZSh1bmxpc3Qoc3Ryc3BsaXQoYXMuY2hhcmFjdGVyKHEzX21vdmllc09ubHkkR2VucmUpLCIsICIpKSkpCmxpc3RBbGxfZCA9IGxpc3QoKQpmb3IgKGNvbHVtbiBpbiBsaXN0QWxsKQogIGxpc3RBbGxfZFtbY29sdW1uXV0gPC0gc3VtKHEzbW92aWVzT25seVssZ2V0KGNvbHVtbildKQpsaXN0QWxsX2QgPSBsaXN0QWxsX2Rbb3JkZXIoc2FwcGx5KGxpc3RBbGxfZCwgbWF4KSldCmd0YWIgPSBkYXRhLmZyYW1lKG1lbHQobGlzdEFsbF9kKSkKZ3RhYiA9IGhlYWQoZ3RhYltvcmRlcihndGFiJHZhbHVlLCBkZWNyZWFzaW5nID0gVFJVRSksXSwxMCkKZ3RhYiRHZW5yZVBjdCA9IGd0YWIkdmFsdWUvc3VtKGd0YWIkdmFsdWUpCnByaW50KGd0YWIpCnAzUTEgPSBnZ3Bsb3QoZ3RhYiwgYWVzKEwxLCBHZW5yZVBjdCkpICsgZ2VvbV9iYXIoc3RhdCA9ICJpZGVudGl0eSIpICsgY29vcmRfZmxpcCgpICsgeGxhYigiR2VucmUiKSArIGxhYnModGl0bGU9InAzUTE6IFRvcDEwIG1vc3QgY29tbW9uIEdlbnJlcyIsIHN1YnRpdGxlID0gIk1vdmllcyBPbmx5IERhdGFzZXQiKQpwcmludChwM1ExKQpgYGAKCkV4YW1pbmUgaG93IHRoZSBkaXN0cmlidXRpb24gb2YgYFJ1bnRpbWVgIGNoYW5nZXMgYWNyb3NzIGdlbnJlcyBmb3IgdGhlIHRvcCAxMCBtb3N0IGNvbW1vbiBnZW5yZXMuCgpgYGB7cn0KbXJ1bnRpbWUgPSBzdWJzZXQocTNtb3ZpZXNPbmx5LCAhaXMubmEocTNtb3ZpZXNPbmx5JFJ1bnRpbWUpKQpydW50aW1lR2VucmUgPSBsaXN0KCkKZm9yIChjb2x1bW4gaW4gZ3RhYiRMMSkgICNsaXN0IHRvcDEwIEdlbnJlIHZzIFJ1bnRpbWUgdmFsdWVzIAogIHJ1bnRpbWVHZW5yZVtbY29sdW1uXV0gPC0gbXJ1bnRpbWUkUnVudGltZVttcnVudGltZVssZ2V0KGNvbHVtbildPT0xXQp4R1I9ZGF0YS5mcmFtZShtZWx0KHJ1bnRpbWVHZW5yZSkpCgpwM1EyX2EgPSBnZ3Bsb3QoeEdSLCBhZXModmFsdWUpKSArIGdlb21faGlzdG9ncmFtKGJpbndpZHRoID0gMykgKyBmYWNldF9ncmlkKEwxfi4sIHNjYWxlcyA9ICJmcmVlX3kiKSArIHhsYWIoIlJ1bnRpbWUiKSArIGxhYnModGl0bGU9InAzUTI6IFRvcDEwIG1vc3QgY29tbW9uIEdlbnJlcyBWcyBSdW50aW1lIiwgc3VidGl0bGUgPSAiTW92aWVzIE9ubHkgRGF0YXNldCIpCnByaW50KHAzUTJfYSkKCnAzUTJfYiA9IGdncGxvdCh4R1IsIGFlcyhMMSwgdmFsdWUpKSArIGdlb21fYm94cGxvdCgpICArIGNvb3JkX2ZsaXAoKSArIHhsYWIoIkdlbnJlIikgKyB5bGFiKCJSdW50aW1lIikgKyBsYWJzKHRpdGxlPSJwM1EyOiBUb3AxMCBHZW5yZSBWcyBSdW50aW1lIEJveFBsb3QiKQpwcmludChwM1EyX2IpCgpgYGAKCkRyYW1hIGlzIHRoZSB0b3AgMXN0IGluIHRoZSBjb21tb24gR2VucmUgbGlzdCBmb2xsb3dlZCBieSBjb21lZHkuICBUaGUgcnVudGltZSBkaXN0cmlidXRpb24gb2YgdGhlIHRvcCAxMCBHZW5yZXMgYXJlIG5vcm1hbCBkaXN0aWJ1dGlvbiAoYmVsbCBjdXJ2ZSkgZXhjZXB0IGZvciBDb21lZHkgYW5kIERvY3VtZW50YXJ5IHdoaWNoIHNob3dzIHRoZSBwcmVzZW5jZSBvZiBiaW1vZGFsaXR5IGluIHRoZSBkaXN0cmlidXRpb24uICBUaGUgcHJlc2VuY2Ugb2YgaHVnZSBudW1iZXJzIG9mIG91dGxpZXJzIGluIGFsbCB0aGUgR2VucmUgYWZmZWN0cyBhbnkgbm90aWNlYWJsZSB0cmVuZHMgaW4gdGhlIHJ1bnRpbWUgYnV0IG1ham9yaXR5IG9mIHRoZSB0b3AgMTAgR2VucmUgYWxtb3N0IGhhdmUgdGhlIHNhbWUgbW92aWUgUnVudGltZSBtZWRpYW4gcmFuZ2VzICB0byBhcHByb3ggOTAtMTEwIG1pbnV0ZXMgYnV0IG9ubHkgZmV3IGV4Y2VwdGlvbnMgYXJlIEFuaW1hdGlvbiBhbmQgU2hvcnQuIAoKIyMgNC4gRWxpbWluYXRlIG1pc21hdGNoZWQgcm93cwoKVGhlIGRhdGFmcmFtZSB3YXMgcHV0IHRvZ2V0aGVyIGJ5IG1lcmdpbmcgdHdvIGRpZmZlcmVudCBzb3VyY2VzIG9mIGRhdGEgYW5kIGl0IGlzIHBvc3NpYmxlIHRoYXQgdGhlIG1lcmdpbmcgcHJvY2VzcyB3YXMgaW5hY2N1cmF0ZSBpbiBzb21lIGNhc2VzICh0aGUgbWVyZ2Ugd2FzIGRvbmUgYmFzZWQgb24gbW92aWUgdGl0bGUsIGJ1dCB0aGVyZSBhcmUgY2FzZXMgb2YgZGlmZmVyZW50IG1vdmllcyB3aXRoIHRoZSBzYW1lIHRpdGxlKS4gVGhlIGZpcnN0IHNvdXJjZeKAmXMgcmVsZWFzZSB0aW1lIHdhcyByZXByZXNlbnRlZCBieSB0aGUgY29sdW1uIGBZZWFyYCAobnVtZXJpYyByZXByZXNlbnRhdGlvbiBvZiB0aGUgeWVhcikgYW5kIHRoZSBzZWNvbmQgYnkgdGhlIGNvbHVtbiBgUmVsZWFzZWRgIChzdHJpbmcgcmVwcmVzZW50YXRpb24gb2YgcmVsZWFzZSBkYXRlKS4KCmBgYHtyfQpxNF9tb3ZpZXNPbmx5ID1xM21vdmllc09ubHkKcTRfbW92aWVzT25seSA9IHN1YnNldChxNF9tb3ZpZXNPbmx5LCAhaXMubmEocTRfbW92aWVzT25seSRSZWxlYXNlZCkpCnE0X21vdmllc09ubHkkUmVsZWFzZWQgPSBhcy5udW1lcmljKHN1YnN0cihxNF9tb3ZpZXNPbmx5JFJlbGVhc2VkLCAwLHJlZ2V4cHIoICItIixxNF9tb3ZpZXNPbmx5JFJlbGVhc2VkKVsxXS0xKSkKeWVhck1hdGNoID0gc3Vic2V0KHE0X21vdmllc09ubHksIFJlbGVhc2VkID09IFllYXIgfCBHcm9zcz49MCB8IFJlbGVhc2VkID09WWVhcisxKQpjYXQoIk1vdmllIHJvd3Mgd2l0aCBtaXNtYXRjaCBZZWFyLVJlbGVhc2VkIGRhdGVzIDoiLCBucm93KHE0X21vdmllc09ubHkpIC0gbnJvdyh5ZWFyTWF0Y2gpKQpgYGAKClJlbW92YWwgbG9naWMgYXJlIHRoZSBmb2xsb3dpbmc6IFJlbW92ZSBOQSdzIGZyb20gdGhlICJSZWxlYXNlZCIiIGNvbHVtbiBvZiB0aGUgb3JpZ2luYWwgZGF0YXNldC4gIEV4dHJhY3QgIG9ubHkgdGhlIFllYXIgZGF0YSBpbiB0aGUgIlJlbGVhc2VkIiBkYXRlcyB1c2luZyByZWdleHByIGFuZCBzdWJzdHIgYW5kIGNvbnZlcnQgdGhvc2UgaW50byBudW1lcmljLiAgQ29tcGFyZSBjb2x1bW4gIlllYXIiIHRvIHRoZSBjb252ZXJ0ZWQgIlJlbGVhc2VkIiBjb2x1bW4gY29udGFpbmluZyBvbmx5IHRoZSBZZWFyIGRhdGEuICBBbGwgcm93cyB3aGVyZSAiWWVhciIgY29sdW1uIHZhbHVlIHRoYXQgYXJlIG5vdCBlcXVhbCB0byB0aGUgIlJlbGVhc2VkIiBZZWFyIGNvbHVtbiB2YWx1ZSBhcmUgbWlzbWF0Y2ggcm93cyBhbmQgd2VyZSByZW1vdmVkLiBBIGxpdHRsZSBiaXQgb2Ygc2xhY2sgb24gc29tZSBtaXNtYXRjaCByb3dzIHdoZXJlIG1vdmllIEdyb3NzIGFyZSBncmVhdGVyIHRoYW4gb3IgZXF1YWwgdG8gemVybyBhcmUgbm90IGluY2x1ZGVkIGZvciByZW1vdmFsLiAgVGhpcyBlbmRzIHVwIHJlbW92aW5nIDE3Mzkgcm93cy4KCiMjIDUuIEV4cGxvcmUgYEdyb3NzYCByZXZlbnVlCgpGb3IgdGhlIGNvbW1lcmNpYWwgc3VjY2VzcyBvZiBhIG1vdmllLCBwcm9kdWN0aW9uIGhvdXNlcyB3YW50IHRvIG1heGltaXplIEdyb3NzIHJldmVudWUuIEludmVzdGlnYXRlIGlmIEdyb3NzIHJldmVudWUgaXMgcmVsYXRlZCB0byBCdWRnZXQsIFJ1bnRpbWUgb3IgR2VucmUgaW4gYW55IHdheS4KCk5vdGU6IFRvIGdldCBhIG1lYW5pbmdmdWwgcmVsYXRpb25zaGlwLCB5b3UgbWF5IGhhdmUgdG8gcGFydGl0aW9uIHRoZSBtb3ZpZXMgaW50byBzdWJzZXRzIHN1Y2ggYXMgc2hvcnQgdnMuIGxvbmcgZHVyYXRpb24sIG9yIGJ5IGdlbnJlLCBldGMuCgpgYGB7cn0KcTVkZiA9IHN1YnNldCh5ZWFyTWF0Y2gsICFpcy5uYShCdWRnZXQpICYgIWlzLm5hKEdyb3NzKSkKcmZfZGYgPSBkYXRhLmZyYW1lKEJ1ZGdldCA9IHE1ZGYkQnVkZ2V0LAogICAgICAgICAgICAgICAgIEdyb3NzID0gcTVkZiRHcm9zcykKcmZfZGZfcGx0ID0gZ2dwbG90KHJmX2RmLCBhZXMoQnVkZ2V0LCBHcm9zcykpICsgZ2VvbV9wb2ludCgpICsgZ2VvbV9zbW9vdGgobWV0aG9kID0gImxtIikgKyBsYWJzKHRpdGxlPSJwNVExOiBNb3ZpZSBCdWRnZXQgVnMgR3Jvc3MgUmV2ZW51ZSIgLCBzdWJ0aXRsZSA9ICJNb3ZpZXMgT25seSBEYXRhc2V0IikgCnByaW50KHJmX2RmX3BsdCkKCnE1ZGYxID0gc3Vic2V0KHllYXJNYXRjaCwgIWlzLm5hKFJ1bnRpbWUpICYgIWlzLm5hKEdyb3NzKSkKcmZfZGYxID0gZGF0YS5mcmFtZShSdW50aW1lID0gcTVkZjEkUnVudGltZSwgR3Jvc3MgPSBxNWRmMSRHcm9zcykKcmZfZGYxX3BsdCA9IGdncGxvdChyZl9kZjEsIGFlcyhSdW50aW1lLCBHcm9zcykpICsgZ2VvbV9wb2ludCgpICsgZ2VvbV9zbW9vdGgobWV0aG9kID0gImxtIikgKyBsYWJzKHRpdGxlPSJwNVExOiBNb3ZpZSBSdW50aW1lIFZzIEdyb3NzIFJldmVudWUiICwgc3VidGl0bGUgPSAiTW92aWVzIE9ubHkgRGF0YXNldCIpIApwcmludChyZl9kZjFfcGx0KQoKZ2JyX2RmID0gZGF0YS5mcmFtZShHZW5yZT1jaGFyYWN0ZXIoKSwgQnVkZ2V0PW51bWVyaWMoKSwgR3Jvc3M9bnVtZXJpYygpLCBzdHJpbmdzQXNGYWN0b3JzID0gRkFMU0UpCgpmb3IgKGNvbHVtbiBpbiBndGFiJEwxKSAjdG9wMTAgR2VucmUgbGlzdCB2cyBCdWRnZXQgdnMgR3Jvc3MKICBnYnJfZGYgPC0gcmJpbmQoZ2JyX2RmLCBkYXRhLmZyYW1lKEdlbnJlID0gY29sdW1uLCBCdWRnZXQ9IHE1ZGYkQnVkZ2V0W3E1ZGZbLGdldChjb2x1bW4pXSA9PSAxXSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgR3Jvc3M9IHE1ZGYkR3Jvc3NbcTVkZlssZ2V0KGNvbHVtbildID09IDFdKSkKCgpkID0gZ2dwbG90KGdicl9kZiwgYWVzKEJ1ZGdldCwgR3Jvc3MsIGZpbGw9IEdlbnJlKSkgKyBnZW9tX3BvaW50KCkgKyBnZW9tX3Ntb290aChtZXRob2QgPSAibG0iKSArIGZhY2V0X2dyaWQofkdlbnJlLCBzY2FsZXMgPSAiZnJlZV95IikKY29ycyA9IGRkcGx5KGdicl9kZiwgYygiR2VucmUiKSwgc3VtbWFyaXNlLCBjb3IgPSByb3VuZChjb3IoQnVkZ2V0LCBHcm9zcyksIDIpKQpkcGx0ID0gZCArIGdlb21fdGV4dChkYXRhPWNvcnMsIGFlcyhsYWJlbD1wYXN0ZSgicj0iLCBjb3IsIHNlcD0iIikpLCB4PTJlKzA4LCB5PTJlKzA5KQpwcmludChkcGx0KQpgYGAKSXQgaXMgaW50ZXJlc3RpbmcgdG8gc2VlIGluIHRoZSBwbG90IHRoYXQgbW92aWUgQnVkZ2V0IGhhcyBhbiBpbXBhY3QgdG8gdGhlIHJldmVudWVzIHByb2R1Y2VkIGJ5IHRoZSBNb3ZpZXMuICBJbmNyZWFzZSBpbiBCdWRnZXQgdGVuZCB0byBpbmNyZWFzZSByZXZlbnVlIHdoaWNoIG1ha2Ugc2Vuc2UgaW4gcmVhbGl0eSBzaW5jZSB0aGVyZSBhcmUgaGlnaCBidWRnZXQgZmlsbXMgdGhhdCBhcmUgdG9wIEdyb3NzZXJzIGFsdGhvdWdoIHRoaXMgY2FuIGJlIGFsc28gYmUgYWZmZWN0ZWQgYnkgdGhlIHF1YWxpdHkgb2YgdGhlIHN0b3J5LCB0aGUgY2FzdCBhbmQgb3RoZXIgcmVzb3VyY2VzLiAgQnkgcGxvdHRpbmcgdGhlIEdyb3NzIFZzIEJ1ZGdldCBwZXIgdG9wIDEwIGNvbW1vbiBHZW5yZSwgaXQgY2FuIGJlIHNlZW4gdGhhdCBBY3Rpb24sIEFkdmVudHVyZSBhbmQgVGhyaWxsZXIgZmlsbXMgaGFzIGEgaGlnaCBSIHZhbHVlIHdoaWNoIHRoZSBwcm9kdWNlciBjYW4gYWxsb2NhdGUgYnVkZ2V0IHRvIHByb2R1Y2UgcXVhbGl0eSBBY3Rpb24sIFRocmlsbGVyIGFuZCBBZHZlbnR1cmUgbW92aWVzIHRoYXQgaGFzIHRoZSBwb3RlbnRpYWwgdG8gcHJvZHVjZSBoaWdoIHJldmVudWVzLiAgRG9jdW1lbnRhcnkgbW92aWVzIGFsc28gaGFzIGEgaGlnaCBSIHZhbHVlIHRoYXQgaGFzIHBvdGVudGlhbCB0byBwcm9kdWNlIHJldmVudWUgYXMgd2VsbC4gIFRoZSBvdmVyYWxsIG1vdmllIEdyb3NzIHJldmVudWUgcGVyIG1vbnRoIHRlbmQgdG8gZm9sbG93IGEgc2ludXNvaWRhbCBwYXR0ZXJuIHdoZXJlIEdyb3NzIHJldmVudWUgYXJlIGxvdyBvbiBtb250aHMgSmFuLUFwciwgaGlnaCBvbiBtb250aHMgTWF5LUp1bmUsIGxvdyBvbiBzdWNjZWRpbmcgbW9udGhzIGFuZCBjb21lcyBiYWNrIGhpZ2ggb24gTm92LURlYy4gCmBgYHtyfQpxNSA9IG1vdmllc09ubHkKcTVkZjIgPSBzdWJzZXQocTUsICFpcy5uYShSZWxlYXNlZCkgJiAhaXMubmEoR3Jvc3MpKQpxNWRmMiRSZWxlYXNlZCA9IGFzLm51bWVyaWMoc3Vic3RyKHE1ZGYyJFJlbGVhc2VkLHJlZ2V4cHIoICItIixxNWRmMiRSZWxlYXNlZClbMV0rMSwgcmVnZXhwciggIi0iLHE1ZGYyJFJlbGVhc2VkKVsxXSsyKSkKZGZfcmcgPSBjKHE1ZGYyJEdyb3NzLCBxNWRmMiRSZWxlYXNlZCkKZGZfcmc9ZGF0YS5mcmFtZShxNWRmMltjKCJSZWxlYXNlZCIsICJHcm9zcyIpXSkKCmRmX3JnX3BsdD1nZ3Bsb3QoZGZfcmcsIGFlcyh4PVJlbGVhc2VkLCB5PUdyb3NzKSkgKyBnZW9tX3BvaW50KGFlcyhjb2xvdXI9R3Jvc3MpKSsgc2NhbGVfeF9jb250aW51b3VzKGJyZWFrcyA9IHNlcSgxLDEyLCBieT0xKSkgKyAgc2NhbGVfY29sb3VyX2dyYWRpZW50KGxvdyA9ICJyZWQiLCBoaWdoID0gImdyZWVuIikgKyB4bGFiKCJSZWxlYXNlZCBNb250aCIpICsgbGFicyh0aXRsZT0icDVRMjogTW92aWUgUmVsZWFzZWQgTW9udGggVnMgR3Jvc3MgUmV2ZW51ZSIgLCBzdWJ0aXRsZSA9ICJNb3ZpZXMgT25seSBEYXRhc2V0IikgCnByaW50KGRmX3JnX3BsdCkKYGBgCgojIyA2LiBQcm9jZXNzIGBBd2FyZHNgIGNvbHVtbgoKVGhlIHZhcmlhYmxlIGBBd2FyZHNgIGRlc2NyaWJlcyBub21pbmF0aW9ucyBhbmQgYXdhcmRzIGluIHRleHQgZm9ybWF0LiBDb252ZXJ0IGl0IHRvIDIgbnVtZXJpYyBjb2x1bW5zLCB0aGUgZmlyc3QgY2FwdHVyaW5nIHRoZSBudW1iZXIgb2Ygd2lucywgYW5kIHRoZSBzZWNvbmQgY2FwdHVyaW5nIG5vbWluYXRpb25zLiBSZXBsYWNlIHRoZSBgQXdhcmRzYCBjb2x1bW4gd2l0aCB0aGVzZSBuZXcgY29sdW1ucywgYW5kIHRoZW4gc3R1ZHkgdGhlIHJlbGF0aW9uc2hpcCBvZiBgR3Jvc3NgIHJldmVudWUgd2l0aCByZXNwZWN0IHRvIHRoZW0uCgpOb3RlIHRoYXQgdGhlIGZvcm1hdCBvZiB0aGUgYEF3YXJkc2AgY29sdW1uIGlzIG5vdCBzdGFuZGFyZDsgeW91IG1heSBoYXZlIHRvIHVzZSByZWd1bGFyIGV4cHJlc3Npb25zIHRvIGZpbmQgdGhlIHJlbGV2YW50IHZhbHVlcy4KCmBgYHtyfQpxNiA9IHllYXJNYXRjaApxNiRBd2FyZHNbcTYkQXdhcmRzID09ICdOL0EnXSA9IDAKcTZkZiA9IGRhdGEuZnJhbWUoQXdhcmRzID0gcTYkQXdhcmRzLCBHcm9zcyA9IHE2JEdyb3NzKQpxNmRmJHdpbnMgPSBhcy5udW1lcmljKHN1YnN0cihxNmRmJEF3YXJkcywgcmVnZXhwcigid2luIiwgcTZkZiRBd2FyZHMpLTMscmVnZXhwcigid2luIiwgcTZkZiRBd2FyZHMpLTEpKQpxNmRmJG5vbWluYXRpb25zID0gYXMubnVtZXJpYyhzdWJzdHIocTZkZiRBd2FyZHMsIHJlZ2V4cHIoIm5vbWluYXRpb24iLCBxNmRmJEF3YXJkcyktMyxyZWdleHByKCJub21pbmF0aW9uIiwgcTZkZiRBd2FyZHMpLTEpKQpjYXQoInZhbGlkL25vbi16ZXJvIHdpbnMgb3Igbm9taW5hdGlvbnMgOiIsIG5yb3coc3Vic2V0KHE2ZGYsICFpcy5uYShub21pbmF0aW9ucykgfCFpcy5uYSh3aW5zKSkpKQp3aW5zTm9tX3RhYiA9IHE2ZGZbY29tcGxldGUuY2FzZXMocTZkZiksXQpzdHIod2luc05vbV90YWJbLC0xXSkgCmBgYApEYXRhc2V0IHVzZWQgaXMgdGhlIG1vdmllIG9ubHkgZGF0YSBzZXQgZnJvbSBxdWVzdGlvbiA0LiBOdW1iZXIgb2Ygd2lucyBhbmQgbm9taW5hdGlvbnMgaW4gdGhlIEF3YXJkIGNvbHVtbiBhcmUgZXh0cmFjdGVkIHVzaW5nIHJlZ2V4cHIgYW5kIHN1YnN0ci4gIEF3YXJkcyBjb2x1bW4gY2hhciAiTi9BIiB2YWx1ZXMgYXJlIGNvbnZlcnRlIHRvIDAsIHRoZW4gZXh0cmFjdGluZyB0aGUgbnVtYmVycyBiZWZvcmUgdGhlICJ3aW5zIiIgYW5kICJub21pbmF0aW9uIiBjaGFyYXRlcnMsIHNlcGFyYXRpbmcgdGhvc2UgdG8gdHdvIGNvbHVtbnMgYW5kIGNvbnZlcnRpbmcgdGhlIHJlc3VsdCB0byBudW1lcmljLiAgVmFsaWQvbm9uLXplcm8gd2luZCBlbmRzIHVwIHRvIDEyOTA1IHJvd3MuCgpgYGB7cn0KcTZkZjEgPSBzdWJzZXQocTZkZiwhaXMubmEoR3Jvc3MpKQpxNmRmMVtpcy5uYShxNmRmMSldIDwtIDAgI2NvbnZlcmluZyBhbGwgTkEgaW4gd2lucyBhbmQgbm9taW5hdGlvbnMgY29sdW1uIHRvIDAKCnE1ZGYxX3BsdCA9IHFwbG90KGxvZyhHcm9zcyksIHdpbnMsIHNpemU9SSgxKSwgZGF0YSA9IHE2ZGYxLCBnZW9tID0gImppdHRlciIpICtsYWJzKHRpdGxlPSJwNVEyOiBHcm9zcyBSZXZlbnVlIFZzIEF3YXJkIFdpbm5pbmdzIiAsIHN1YnRpdGxlID0gIk1vdmllcyBPbmx5IERhdGFzZXQiKSAKcHJpbnQocTVkZjFfcGx0KQoKcTVkZjFfcGx0MSA9IHFwbG90KGxvZyhHcm9zcyksIG5vbWluYXRpb25zLCBzaXplPUkoMSksIGRhdGEgPSBxNmRmMSwgZ2VvbSA9ICJqaXR0ZXIiKStsYWJzKHRpdGxlPSJwNVEyOiBHcm9zcyBSZXZlbnVlIFZzIEF3YXJkIE5vbWluYXRpb25zIiAsIHN1YnRpdGxlID0gIk1vdmllcyBPbmx5IERhdGFzZXQiKSAKcHJpbnQocTVkZjFfcGx0MSkKYGBgCgpNb3ZpZXMgdGhhdCBhcmUgQXdhcmQgd2lubmVyIGFuZCByZWNlaXZlZCBub21pbmF0aW9ucyBoYWQgaW5jcmVhc2VkIGluIHJldmVudWVzLiAgVGhpcyBtYWtlIHNlbnNlIGJlY2F1c2UgbW92aWVzIHRoYXQgcmVjZWl2ZWQgbW9yZSBBd2FyZCB3aW5uaW5ncyBhbmQgbm9taW5hdGlvbnMgYXJlIGdvb2QgcXVhbGl0eSBtb3ZpZXMgc2luY2UgdGhlc2UgYXdhcmRzIGFyZSBiZWluZyBzdHJlYW1saW5lZCBieSBtb3ZpZSBBY2FkZW15IGJvYXJkcy4gR29vZCBxdWFsaXR5IG1vdmllcyB0ZW5kIHRvIGluY3JlYXNlIG1vdmllIEdyb3NzIHJldmVudWVzLgoKIyMgNy4gTW92aWUgcmF0aW5ncyBmcm9tIElNRGIgYW5kIFJvdHRlbiBUb21hdG9lcwpgYGB7cn0KcTcgPSB5ZWFyTWF0Y2gKcTdkZiA9IGRhdGEuZnJhbWUoR3Jvc3MgPSBxNyRHcm9zcywgSW1kYlJhdGluZyA9IHE3JGltZGJSYXRpbmcsIHRvbWF0b1JhdGluZyA9IHE3JHRvbWF0b1JhdGluZywgdG9tYXRvVXNlclJhdGluZyA9IHE3JHRvbWF0b1VzZXJSYXRpbmcpCnE3ZGYgPXE3ZGZbY29tcGxldGUuY2FzZXMocTdkZiksXQoKcTdkZl9wbHQxPSBnZ3Bsb3QocTdkZiwgYWVzKHg9SW1kYlJhdGluZywgeT10b21hdG9SYXRpbmcpKSArIGdlb21fcG9pbnQoKSArIGdlb21fc21vb3RoKG1ldGhvZCA9ICJsb2VzcyIpICsgbGFicyh0aXRsZT0iTW92aWUgVG9tYXRvIFJhdGluZyBWcyBJTURiIFJhdGluZyIsIHN1YnRpdGxlID0gIk1vdmllcyBPbmx5IERhdGFzZXQiKQpwcmludChxN2RmX3BsdDEpCgpxN2RmX3BsdDI9IGdncGxvdChxN2RmLCBhZXMoeD1JbWRiUmF0aW5nLCB5PXRvbWF0b1VzZXJSYXRpbmcpKSArIGdlb21fcG9pbnQoKSArIGdlb21fc21vb3RoKG1ldGhvZCA9ICJsb2VzcyIpICsgbGFicyh0aXRsZT0iTW92aWUgVG9tYXRvIFVzZXIgUmF0aW5nIFZzIElNRGIgUmF0aW5nIiwgc3VidGl0bGUgPSAiTW92aWVzIE9ubHkgRGF0YXNldCIpCnByaW50KHE3ZGZfcGx0MikKCnE3ZGZfcGx0Mz0gZ2dwbG90KHE3ZGYsIGFlcyh4PUltZGJSYXRpbmcsIHk9R3Jvc3MpKSArIGdlb21fcG9pbnQoKSArIGdlb21fc21vb3RoKG1ldGhvZCA9ICJsb2VzcyIpICsgbGFicyh0aXRsZT0iTW92aWUgVG9tYXRvIFVzZXIgUmF0aW5nIFZzIElNRGIgUmF0aW5nIiwgc3VidGl0bGUgPSAiTW92aWVzIE9ubHkgRGF0YXNldCIpCnByaW50KHE3ZGZfcGx0MykKCnE3ZGZfcGx0ND0gZ2dwbG90KHE3ZGYsIGFlcyh4PXRvbWF0b1JhdGluZywgeT1Hcm9zcykpICsgZ2VvbV9wb2ludCgpICsgZ2VvbV9zbW9vdGgobWV0aG9kID0gImxvZXNzIikgKyBsYWJzKHRpdGxlPSJNb3ZpZSBUb21hdG8gVXNlciBSYXRpbmcgVnMgSU1EYiBSYXRpbmciLCBzdWJ0aXRsZSA9ICJNb3ZpZXMgT25seSBEYXRhc2V0IikKcHJpbnQocTdkZl9wbHQ0KQpgYGAKVGhlIGRpc3Bhcml0eSBpcyBub3QgdGhhdCBoaWdoIGJldHdlZW4gSU1EYiByYXRpbmcgYW5kIHRvbWF0byByYXRpbmdzLiAgIFRoZXkgYXJlIGJvdGggcHJvcG90aW9uYWwgYnkgbG9va2luZyBhdCB0aGUgdHJlbmRzIGluIHRoZSBncmFwaC0gcHJvcG90aW9uYWwgYmVpbmcgaWYgb25lIG9mIHRoZW0gaW5jcmVhc2VzIHRoZSBvdGhlciByYXRpbmcgaW5jcmVhc2VzLiAgIEFzIHBlciBkZXNjcmlwdGlvbiBpbiB0aGUgd2Vic2l0ZSwgSU1EQiB3ZWlnaHRzIHRoZSBhdmVyYWdlIGJhc2VkIG9uIGhvdyBwZW9wbGUgZmlsbCBvdXQgcmF0aW5ncyBhbmQgdG8gYXZvaWQgInZvdGUgc3R1ZmZpbmciICh3ZWlnaHRlZCBhdmVyYWdlIHJhdGluZykuICBSb3R0ZW4gdG9tYXRvZXMgb24gdGhlIG90aGVyIGhhbmQsIGNvdW50IHRoZSBwZXJjZW50YWdlIG9mIHVzZXJzIHRoYXQgcmF0ZSB0aGUgbW92aWUgYXQgYXQgYSBjZXJ0YWluIHRocmVzaG9sZCB0aGF0IGNsZWFybHkgdGhpbmsgdGhlIG1vdmllIGlzICJnb29kIi4gIEluIHRlcm1zIG9mIEdyb3NzIHJldmVudWUgcHJvZHVjZSBwZXIgcmF0aW5ncywgbW92aWUgR3Jvc3MgcmV2ZW51ZSBpbmNyZWFzZSB3aXRoIGluY3JlYXNlIGluIHJhdGluZy4gSU1EQiBkYXRhIHBvaW50cyB3aXRoIGhpZ2ggR3Jvc3MgcmV2ZW51ZSBhcmUgbW9yZSBmb2N1c2VkIHdpdGhpbiA2LjAgdG8gNy4wIHJhdGluZyB3aGlsZSB0b21hdG8gcmF0aW5nIGFyZSBtb3JlIHNjYXR0ZXJlZCB3aXRoaW4gYXJvdW5kIDMuNSB0byA3LjUgYmVjYXVlIHRvbWF0byByYXRpbmcgdGhyZXNob2xkIGZvciAiZ29vZCIgbW92aWVzIiBzdGFydHMgYXQgMy41IGFuZCBoaWdoZXIuIAoKIyMgOC4gUmF0aW5ncyBhbmQgYXdhcmRzCgpUaGVzZSByYXRpbmdzIHR5cGljYWxseSByZWZsZWN0IHRoZSBnZW5lcmFsIGFwcGVhbCBvZiB0aGUgbW92aWUgdG8gdGhlIHB1YmxpYyBvciBnYXRoZXIgb3BpbmlvbnMgZnJvbSBhIGxhcmdlciBib2R5IG9mIGNyaXRpY3MuIFdoZXJlYXMgYXdhcmRzIGFyZSBnaXZlbiBieSBwcm9mZXNzaW9uYWwgc29jaWV0aWVzIHRoYXQgbWF5IGV2YWx1YXRlIGEgbW92aWUgb24gc3BlY2lmaWMgYXR0cmlidXRlcywgc3VjaCBhcyBhcnRpc3RpYyBwZXJmb3JtYW5jZSwgc2NyZWVucGxheSwgc291bmQgZGVzaWduLCBldGMuCmBgYHtyfQpxOCA9IHllYXJNYXRjaAojcTZkZiA9IHN1YnNldChxNiwgIWlzLm5hKEdyb3NzKSkKcTgkQXdhcmRzW3E2JEF3YXJkcyA9PSAnTi9BJ10gPSAwCnE4ZGYgPSBkYXRhLmZyYW1lKEF3YXJkcyA9IHE4JEF3YXJkcywgSW1kYlJhdGluZyA9IHE4JGltZGJSYXRpbmcsIHRvbWF0b1JhdGluZyA9IHE4JHRvbWF0b1JhdGluZywgdG9tYXRvVXNlclJhdGluZyA9IHE4JHRvbWF0b1VzZXJSYXRpbmcgKQoKcThkZiR3aW5zID0gYXMubnVtZXJpYyhzdWJzdHIocTZkZiRBd2FyZHMsIHJlZ2V4cHIoIndpbiIsIHE2ZGYkQXdhcmRzKS0zLHJlZ2V4cHIoIndpbiIsIHE2ZGYkQXdhcmRzKS0xKSkKcThkZiRub21pbmF0aW9ucyA9IGFzLm51bWVyaWMoc3Vic3RyKHE2ZGYkQXdhcmRzLCByZWdleHByKCJub21pbmF0aW9uIiwgcTZkZiRBd2FyZHMpLTMscmVnZXhwcigibm9taW5hdGlvbiIsIHE2ZGYkQXdhcmRzKS0xKSkKcThkZiA9cThkZltjb21wbGV0ZS5jYXNlcyhxOGRmKSxdCnE4X3RhYiA9cThkZlssIC0xXQpxOF90YWJfcGx0MSA9IGdncGFpcnMocThfdGFiKSAjY29ycmVsYXRpb24gbWF0cml4CnByaW50KHE4X3RhYl9wbHQxKQoKcThfdGFiX3BsdDIgPSBnZ3Bsb3QocThfdGFiLCBhZXMoeD1JbWRiUmF0aW5nLCB5PXdpbnMpKSArIGdlb21fcG9pbnQoYWVzKGNvbG91cj13aW5zKSkgKyBzY2FsZV9jb2xvdXJfZ3JhZGllbnQobG93ID0gInJlZCIsIGhpZ2ggPSAiZ3JlZW4iKQpwcmludChxOF90YWJfcGx0MikKCnE4X3RhYl9wbHQyID0gZ2dwbG90KHE4X3RhYiwgYWVzKHg9dG9tYXRvUmF0aW5nLCB5PXdpbnMpKSArIGdlb21fcG9pbnQoYWVzKGNvbG91cj13aW5zKSkgKyBzY2FsZV9jb2xvdXJfZ3JhZGllbnQobG93ID0gInJlZCIsIGhpZ2ggPSAiZ3JlZW4iKQpwcmludChxOF90YWJfcGx0MikKCgpgYGAKVGhlcmUgYXJlIG5vdGljZWFibGUgY29ycmVsYXRpb24gYmV0d2VlbiB0aGUgZGlmZmVyZW50IHJhdGluZyAoSU1EYiBhbmQgVG9tYXRvKSBhZ2FpbnRzIHRoZSBudW1iZXIgb2YgQXdhcmRzICh3aW5zIGFuZCBub21pbmF0aW9uKSByZWNpdmVkLiAgSGlnaCBJTURiIHJhdGluZyBhcm91bmQgNi41IHRvIDcuNSByZWNlaXZlZCBoaWdoIG51bWJlcnMgb2YgQXdhcmQgd2lubmluZ3MgYW5kIG5vbWluYXRpb25zLiAgVG9tYXRvIHJhdGluZ3Mgd2l0aCBoaWdoIEF3YXJkIHdpbm5pbmcgYW5kIG5vbWluYXRpb25zIGFyZSB3aXRoaW4gNy41IHRvIDguNS4gIFRoZSBkaXNwYXJpdHkgYmV0d2VlbiB0aGUgcmF0aW5nIGFyZSBub3QgdGhhdCBoaWdoLgoKIyMgOS4gRXhwZWN0ZWQgaW5zaWdodHMKCmBgYHtyfQpxOV9kZiA9IGRhdGEuZnJhbWUoTGFuZ3VhZ2UgPSB5ZWFyTWF0Y2gkTGFuZ3VhZ2UsIEJ1ZGdldCA9IHllYXJNYXRjaCRCdWRnZXQsIEdyb3NzID0geWVhck1hdGNoJEdyb3NzKQphID0gY1NwbGl0KHE5X2RmLCAiTGFuZ3VhZ2UiLCAiLCAiLCAibG9uZyIpCnE5X2RmID0gZGNhc3QoYSwgLi4uIH4gTGFuZ3VhZ2UsIHZhbHVlLnZhciA9ICJMYW5ndWFnZSIsIGZ1bi5hZ2dyZWdhdGUgPSBsZW5ndGgpCmxhbmdBbGw9c29ydCh1bmlxdWUodW5saXN0KHN0cnNwbGl0KGFzLmNoYXJhY3Rlcih5ZWFyTWF0Y2gkTGFuZ3VhZ2UpLCIsICIpKSkpCmxhbmdBbGxfZGN0ID0gbGlzdCgpCmxhbmdBbGwgPSBsYW5nQWxsW2xhbmdBbGwhPSAiIEFuY2llbnQgKHRvIDE0NTMpIl0KbGFuZ0FsbCA9IGxhbmdBbGxbbGFuZ0FsbCE9ICIgT2xkIl0KZm9yIChjb2x1bW4gaW4gbGFuZ0FsbCkKICBsYW5nQWxsX2RjdFtbY29sdW1uXV0gPC0gc3VtKHE5X2RmWyxnZXQoY29sdW1uKV0pCmxhbmdBbGxfZGN0ID0gbGFuZ0FsbF9kY3Rbb3JkZXIoc2FwcGx5KGxhbmdBbGxfZGN0LCBtYXgpKV0KbGFuZ0FsbF9kY3RfdGFiID0gbmEub21pdChkYXRhLmZyYW1lKG1lbHQobGFuZ0FsbF9kY3QpKSkKbGFuZ0FsbF9kY3RfdGFiW2xhbmdBbGxfZGN0X3RhYj09Ik4vQSJdID0gTkEKbGFuZ0FsbF9kY3RfdGFiPWxhbmdBbGxfZGN0X3RhYltjb21wbGV0ZS5jYXNlcyhsYW5nQWxsX2RjdF90YWIpLF0KbGFuZ0FsbF9kY3RfdGFiID0gaGVhZChsYW5nQWxsX2RjdF90YWJbb3JkZXIobGFuZ0FsbF9kY3RfdGFiJHZhbHVlLCBkZWNyZWFzaW5nID0gVFJVRSksXSwxMCkKbGFuZ0FsbF9kY3RfdGFiJExhbmd1YWdlUGN0ID0gbGFuZ0FsbF9kY3RfdGFiJHZhbHVlL3N1bShsYW5nQWxsX2RjdF90YWIkdmFsdWUpCnByaW50KGxhbmdBbGxfZGN0X3RhYikKCnA5UTFfcGx0ID0gZ2dwbG90KGxhbmdBbGxfZGN0X3RhYiwgYWVzKEwxLCBMYW5ndWFnZVBjdCkpICsgZ2VvbV9iYXIoc3RhdCA9ICJpZGVudGl0eSIpICsgY29vcmRfZmxpcCgpICsgeGxhYigiTW92aWUgTGFuZ3VhZ2UiKSArIGxhYnModGl0bGU9InA5UTE6IFRvcDEwIG1vc3QgY29tbW9uIG1vdmllcyBMYW5ndWVzIiwgc3VidGl0bGUgPSAiTW92aWVzIE9ubHkgRGF0YXNldCIpCnByaW50KHA5UTFfcGx0KQpxOV9kZjEgPSBkYXRhLmZyYW1lKExhbmd1YWdlPWNoYXJhY3RlcigpLCBHcm9zcz1udW1lcmljKCksIEJ1ZGdldD1udW1lcmljKCksIHN0cmluZ3NBc0ZhY3RvcnMgPSBGQUxTRSkKCmZvciAoY29sdW1uIGluIGxhbmdBbGxfZGN0X3RhYiRMMSkgCiAgcTlfZGYxIDwtIHJiaW5kKHE5X2RmMSwgZGF0YS5mcmFtZShMYW5ndWFnZSA9IGNvbHVtbiwgR3Jvc3M9IHE5X2RmJEdyb3NzW3E5X2RmWyxnZXQoY29sdW1uKV0gPT0gMV0sIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIEJ1ZGdldD0gcTlfZGYkQnVkZ2V0W3E5X2RmWyxnZXQoY29sdW1uKV0gPT0gMV0pKQoKZGF0ID0gZ2dwbG90KHE5X2RmMSwgYWVzKEJ1ZGdldCwgR3Jvc3MsIGZpbGw9IExhbmd1YWdlKSkgKyBnZW9tX3BvaW50KCkgKyBnZW9tX3Ntb290aChtZXRob2QgPSAibG0iKSArIGZhY2V0X2dyaWQofkxhbmd1YWdlLCBzY2FsZXMgPSAiZnJlZV95IikKY29ycyA9IGRkcGx5KHE5X2RmMSwgYygiTGFuZ3VhZ2UiKSwgc3VtbWFyaXNlLCBjb3IgPSByb3VuZChjb3IoQnVkZ2V0LCBHcm9zcyksIDIpKQpkYXRfZHBsdCA9IGRhdCArIGdlb21fdGV4dChkYXRhPWNvcnMsIGFlcyhsYWJlbD1wYXN0ZSgicj0iLCBjb3IsIHNlcD0iIikpLCB4PTJlKzA4LCB5PTJlKzA5KQpwcmludChkYXRfZHBsdCkKYGBgCgpNb3ZpZXMgaW4gRW5nbGlzaCBsYW5ndWFnZSBhcmUgdGhlIG1vc3QgY29tbW9uIG1vdmllcyBpbiB0aGUgZGF0YSBzZXQuICBUaGlzIGNvbnN0aXR1dGVzIGFyb3VuZCA3NSUgb2YgdGhlIHRvdGFsIGNvbW1vbiB0b3AgMTAgbGFuZ3VhZ2UgbW92aWVzIGZvbGxvd2VkIGJ5IEZyZW5jaCBhdCA1OSUuCgpCb3RoIG1vdmllIEdyb3NzIGFuZCBCdWRnZXQgYXJlIGhpZ2hseSBjb29ycmVsYXRlZCBhY3Jvc3MgYWxsIHRoZSB0b3AgMTAgbW92aWUgbGFuZ3VhZ2VzLiAgSW5wc2l0ZSBvZiB0aGUgRW5nbGlzaCBtb3ZpZXMgYmVpbmcgdGhlIHRvcCBjb21tb25nIG1vdmllIGxhbmd1YWdlcywgaXQgaGFzIHRoZSBsb3dlc3QgY29ycmVsYXRpb24gYXQgcj03NCUgYW5kIEl0YWxpYW4gaXMgdGhlIGhpZ2hlc3QgYXQgcj04MiUuICBJdCBkb2VzIG1ha2Ugc2Vuc2UgdGhhdCBoaWdoIGJ1ZGdldCBtb3ZpZXMgdGVuZCB0byBwcm9kdWNlcyBnb29kIHJldmVudWVzIGFzIG1vcmUgZG9sbGFyIGNhbiBiZSBhbGxvY2F0ZWQgdG8gcmVzb3VyY2VzIGxpa2UgZ29vZC9mYW1vdXMgQWN0b3JzLCBtb3ZpZSBsb2NhdGlvbnMsIGVxdWlwZW1lbnRzLCBzcGVjaWFsIGVmZmVjdHMgYW5kIHN0YWZmcy4gIFRoZXJlIGFyZSBhbHNvIHNvbWUgZXhjZXB0aW9ucyB3ZXJlIGZldyBoaWdoIGJ1ZGdldGVkIEVuZ2xpc2ggbW92aWVzIHRoYXQgd2hlcmUgZmxvcHBlZCBmb3Igc29tZSByZWFzb24gd2hpY2ggYWZmZWN0IHRoZSByIHZhbHVlIGZvciB0aGUgRW5nbGlzaCBtb3ZpZXMgdG8gYmUgc2xpZ2h0bHkgbG93ZXIgdGhhbiB0aGUgcmVzdCB0aGUgdG9wIHRlbiBjb21tb24gbW92aWUgbGFuZ3VhZ2VzLiAgICAKCgojIyAxMC4gVW5leHBlY3RlZCBpbnNpZ2h0CgpgYGB7cn0KcTEwX2RmID0gZGF0YS5mcmFtZShMYW5ndWFnZSA9IHllYXJNYXRjaCRMYW5ndWFnZSwgQnVkZ2V0ID0geWVhck1hdGNoJEJ1ZGdldCwgR3Jvc3MgPSB5ZWFyTWF0Y2gkR3Jvc3MsIEltZGJSYXRpbmcgPSB5ZWFyTWF0Y2gkaW1kYlJhdGluZywgdG9tYXRvUmF0aW5nID0geWVhck1hdGNoJHRvbWF0b1JhdGluZywgdG9tYXRvVXNlclJhdGluZyA9IHllYXJNYXRjaCR0b21hdG9Vc2VyUmF0aW5nKQpiID0gY1NwbGl0KHExMF9kZiwgIkxhbmd1YWdlIiwgIiwgIiwgImxvbmciKQpxMTBfZGYgPSBkY2FzdChiLCAuLi4gfiBMYW5ndWFnZSwgdmFsdWUudmFyID0gIkxhbmd1YWdlIiwgZnVuLmFnZ3JlZ2F0ZSA9IGxlbmd0aCkKCnExMF9kZjEgPSBkYXRhLmZyYW1lKExhbmd1YWdlPWNoYXJhY3RlcigpLCBCdWRnZXQ9bnVtZXJpYygpLCBHcm9zcz1udW1lcmljKCksIEltZGJSYXRpbmc9bnVtZXJpYygpLCB0b21hdG9SYXRpbmc9bnVtZXJpYygpLCB0b21hdG9Vc2VyUmF0aW5nPW51bWVyaWMoKSwgIHN0cmluZ3NBc0ZhY3RvcnMgPSBGQUxTRSkKCmZvciAoY29sdW1uIGluIGxhbmdBbGxfZGN0X3RhYiRMMSkKICBxMTBfZGYxIDwtIHJiaW5kKHExMF9kZjEsIGRhdGEuZnJhbWUoTGFuZ3VhZ2UgPSBjb2x1bW4sIEJ1ZGdldD0gcTEwX2RmJEJ1ZGdldFtxMTBfZGZbLGdldChjb2x1bW4pXSA9PSAxXSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIEdyb3NzPSBxMTBfZGYkR3Jvc3NbcTEwX2RmWyxnZXQoY29sdW1uKV0gPT0gMV0sIywgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBJbWRiUmF0aW5nID0gcTEwX2RmJEltZGJSYXRpbmdbcTEwX2RmWyxnZXQoY29sdW1uKV0gPT0gMV0sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB0b21hdG9SYXRpbmcgPSBxMTBfZGYkdG9tYXRvUmF0aW5nW3ExMF9kZlssZ2V0KGNvbHVtbildID09IDFdLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHRvbWF0b1VzZXJSYXRpbmcgPSBxMTBfZGYkdG9tYXRvVXNlclJhdGluZ1txMTBfZGZbLGdldChjb2x1bW4pXSA9PSAxXSkpCnExMF9kZjEgPXExMF9kZjFbY29tcGxldGUuY2FzZXMocTEwX2RmMSksXQoKZGF0MTAgPSBnZ3Bsb3QocTEwX2RmMSwgYWVzKEltZGJSYXRpbmcsIHRvbWF0b1JhdGluZywgZmlsbD0gTGFuZ3VhZ2UpKSArIGdlb21fcG9pbnQoKSArIGdlb21fc21vb3RoKG1ldGhvZCA9ICJsbSIpICsgZmFjZXRfZ3JpZCh+TGFuZ3VhZ2UsIHNjYWxlcyA9ICJmcmVlX3kiKQpjb3JzID0gZGRwbHkocTEwX2RmMSwgYygiTGFuZ3VhZ2UiKSwgc3VtbWFyaXNlLCBjb3IgPSByb3VuZChjb3IoSW1kYlJhdGluZywgdG9tYXRvUmF0aW5nKSwgMikpCmRhdDEwX2RwbHQgPSBkYXQxMCArIGdlb21fdGV4dChkYXRhPWNvcnMsIGFlcyhsYWJlbD1wYXN0ZSgicj0iLCBjb3IsIHNlcD0iIikpLCB4PTMsIHk9MSkKcHJpbnQoZGF0MTBfZHBsdCkKCm0xID0gZGRwbHkocTEwX2RmMSwgLihMYW5ndWFnZSksIHN1bW1hcml6ZSwgbWVkID0gbWVkaWFuKEltZGJSYXRpbmcpKQpwMTBfcGx0MSA9IGdncGxvdChxMTBfZGYxLCBhZXMoeD1MYW5ndWFnZSx5PSAgSW1kYlJhdGluZykpICsgZ2VvbV9ib3hwbG90KCkgKyBnZW9tX3RleHQoZGF0YSA9IG0xLCBhZXMoeSA9IG1lZCwgbGFiZWwgPSByb3VuZChtZWQsMikpLHNpemUgPSAzLCB2anVzdCA9IC0wLjUpICsgbGFicyh0aXRsZT0icDEwUTE6IFRvcDEwIG1vc3QgY29tbW9uIG1vdmllcyBMYW5ndWVzIFZzIEltZGJSYXRpbmciLCBzdWJ0aXRsZSA9ICJNb3ZpZXMgT25seSBEYXRhc2V0IikKcHJpbnQocDEwX3BsdDEpCiAgCgptMiA9IGRkcGx5KHExMF9kZjEsIC4oTGFuZ3VhZ2UpLCBzdW1tYXJpemUsIG1lZCA9IG1lZGlhbih0b21hdG9SYXRpbmcpKQpwMTBfcGx0MiA9IGdncGxvdChxMTBfZGYxLCBhZXMoeD1MYW5ndWFnZSx5PSAgdG9tYXRvUmF0aW5nKSkgKyBnZW9tX2JveHBsb3QoKSArIGdlb21fdGV4dChkYXRhID0gbTIsIGFlcyh5ID0gbWVkLCBsYWJlbCA9IHJvdW5kKG1lZCwyKSksc2l6ZSA9IDMsdmp1c3QgPSAtMC41KSArIAogIGxhYnModGl0bGU9InAxMFExOiBUb3AxMCBtb3N0IGNvbW1vbiBtb3ZpZXMgTGFuZ3VlcyBWcyB0b21hdG9SYXRpbmciLCBzdWJ0aXRsZSA9ICJNb3ZpZXMgT25seSBEYXRhc2V0IikKcHJpbnQocDEwX3BsdDIpCgptMyA9IGRkcGx5KHExMF9kZjEsIC4oTGFuZ3VhZ2UpLCBzdW1tYXJpemUsIG1lZCA9IG1lZGlhbih0b21hdG9Vc2VyUmF0aW5nKSkKcDEwX3BsdDMgPSBnZ3Bsb3QocTEwX2RmMSwgYWVzKHg9TGFuZ3VhZ2UseT0gIHRvbWF0b1VzZXJSYXRpbmcpKSArIGdlb21fYm94cGxvdCgpICsgZ2VvbV90ZXh0KGRhdGEgPSBtMywgYWVzKHkgPSBtZWQsIGxhYmVsID0gcm91bmQobWVkLDIpKSxzaXplID0gMyx2anVzdCA9IC0wLjUpICsgCiAgbGFicyh0aXRsZT0icDEwUTE6IFRvcDEwIG1vc3QgY29tbW9uIG1vdmllcyBMYW5ndWVzIFZzIHRvbWF0b1VzZXJSYXRpbmciLCBzdWJ0aXRsZSA9ICJNb3ZpZXMgT25seSBEYXRhc2V0IikKcHJpbnQocDEwX3BsdDMpCgoKYGBgCgpTdXJwcmlzaW5nIHRvIHNlZSB0aGF0IENoaW5lc2UgbW92aWVzIGluIE1hbmRhcmFpbiBvciBDYW50b25lc2UgbGFuZ3VlYWdlcyBhcmUgbm90IGluIHRoZSB0b3AgdGVuIGNvbW1vbiBtb3ZpZXMgaW4gdGhlIGRhdGFzZXQuICBJbnNwaXRlIG9mIGJlaW5nIGEgbGFyZ2UgY291bnRyeSwgSSBrbm93IHRoYXQgdGhlcmUgbG90cyBvZiBtb3ZpZXMgcHJvZHVjZWQgcGVyIHllYXIgYW5kIGhhdmUgc2VlbiBhIGxvdCBvZiB0aGVtIHRoYXQgd2VyZSBkdWJiZWQgaW4gRW5nbGlzaCBsaWtlIHRob3NlIGZhbW91cyBBY3RvcnMgbGlrZSBKYWNraWUgQ2hhbiwgQnJ1Y2UgTGVlIG9yIEpldCBMaS4gIEkgdGhpbmsgdGhhdCBmdXJ0aGVyIGludmVzdGdhdGlvbiBhcmUgbmVlZGVkIGFib3V0IHRoZSBMYW5ndWFnZSBhcyBjYXRlZ29yeSBpbiB0aGUgZGF0YWJhc2UgaS5lIGlzIHRoZSBsYW5ndWFnZSBhcmUgYmFzZWQgb24gdGhlIG9yaWduYWwgbGFuZ3VhZ2Ugc3Bva2VuIG9yIGR1YmJlZCBpbiBFbmdsaXNoPy4KICAgICAgSW4gYWRkaXRpb24sIGJhc2VkIG9uIHRoZSBhYm92ZSBib3ggcGxvdHMsIHN1cnByaXNpbmcgdG8gc2VlIHRoYXQgYW1vbmcgdGhlIGNvbW1vbiB0b3AgdGVuIGxhbmd1YWdlLCBFbmdsaXNoIGxhbmd1YWdlIG1vdmllcyBpcyBvbmUgb2YgYW1vbmcgdGhlIGxvd2VzdCBpbiBhbGwgdGhlIHJhdGluZ3MgaW5zcGl0ZSBvZiBiZWluZyBvbiB0aGUgdG9wIGNvbW1vbiBtb3ZpZSBsYW5ndWFnZXMuICBFbmdsaXNoIGxhbmd1YWdlIG1vdmllcyBJTURiIHJhdGluZyBtZWRpYW4gb2YgNi41IChoaWdoZXN0IGFyZSBIaW5kaSwgUG9saXNoIGF0IDcuMTUgJiA3ICkgLCAgRW5nbGlzaCBsYW5ndWFnZSBtb3ZpZXMgVG9tYXRvIHJhdGluZyBtZWRpYW4gb2YgNS44IChoaWdoZXN0IGlzIFBvbGlzaCBhdCA2LjU1KSBhbmQgIEVuZ2xpc2ggbGFuZ3VhZ2UgbW92aWVzIFRvbWF0byBVc2VyIHJhdGluZyBtZWRpYW4gb2YgMy4zIChoaWdoZXN0IGFyZSBIaW5kaSwgUG9saXNoIGF0IDMuNjUgJiAzLjU1ICkuICBCb3RoIEhpbmRpIG9yIFBvbGlzaCBzZWVtcyB0byBiZSBjb25zaXN0ZW50bHkgaGlnaGVzaCBhbW9uZyBmb3IgYWxsIHRoZSAzIHJhdGluZ3MuCgoK